package org.redis.cluster.extension;

import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.redis.cluster.Constants;
import org.redis.cluster.RedisCluster;
import org.redis.cluster.OrderSorter;
import org.redis.cluster.extension.exception.ExtensionException;
import org.redis.cluster.extension.helper.ExtensionLoaderHelper;
import org.redis.cluster.plugin.Plugin;
import org.redis.cluster.util.ClassUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class PluginExtensionLoader implements ExtensionLoader {
	
	private final static String PLUGIN_EXTENSION_CONFIG=String.format("META-INF/%s/plugins", Constants.NAME);
	
	protected final Logger logger=LoggerFactory.getLogger(getClass());
	
	private final Collection<Plugin> pluginInstances=new LinkedList<Plugin>();
	
	private RedisCluster redisCluster;
	private ExtensionLoader extensionLoader;
	
	public PluginExtensionLoader(RedisCluster redisCluster,ExtensionLoader extensionLoader) {
		this.redisCluster=redisCluster;
		this.extensionLoader=extensionLoader;
	}
	
	@Override
	public <T> Collection<T> load(Class<T> type) {
		Collection<T> result=extensionLoader.load(type);
		if(CollectionUtils.isEmpty(result)) {
			return Collections.emptyList();
		}
		
		for(T instance:result) {
			if(isPlugin(instance)) {
				doPluginProcess(instance);
			}
		}
		
		return result;
	}

	@Override
	public <T> T load(Class<T> type, Object... parameters) {
		T instance=extensionLoader.load(type, parameters);
		if(!isPlugin(instance)) {
			return instance;
		}
		
		doPluginProcess(instance);
		return instance;
	}

	@Override
	public <T> T load(String name, Class<T> type, Object... parameters) {
		T instance=extensionLoader.load(name, type, parameters);
		if(!isPlugin(instance)) {
			return instance;
		}
		
		doPluginProcess(instance);
		return instance;
	}
	
	@Override
	public void init() {
		doPluginInit();
		extensionLoader.init();
	}
	
	@Override
	public void destroy() {
		extensionLoader.destroy();
		doPluginDestroy();
	}
	
	protected void doPluginInit() {
		Enumeration<URL> urls;
		
		try {
			urls=getClass().getClassLoader().getResources(PLUGIN_EXTENSION_CONFIG);
		}
		catch(Exception e) {
			throw new ExtensionException(e);
		}
		
		if(urls==null) {
			return;
		}
		
		try {
			while(urls.hasMoreElements()) {
				URL url=urls.nextElement();
				
				if(logger.isDebugEnabled()) {
					logger.debug("准备读取插件 - {}",url);
				}
				
				Map<String,Class<?>> pluginClasses=doReadPluginClasses(url);
				if(MapUtils.isEmpty(pluginClasses)) {
					continue;
				}
				
				for(Entry<String,Class<?>> entry:pluginClasses.entrySet()) {
					Class<?> clazz=entry.getValue();
					if(!isPlugin(clazz)) {
						continue;
					}
					
					if(logger.isDebugEnabled()) {
						logger.debug("找到插件 - {}",clazz.getName());
					}
					
					Plugin pluginInstance=ClassUtil.newInstance(clazz);
					doPluginProcess(pluginInstance);
				}
			}
		}
		catch(Exception e) {
			throw new ExtensionException(e);
		}
	}
	
	protected Map<String,Class<?>> doReadPluginClasses(URL url) throws Exception {
		return ExtensionLoaderHelper.read(url);
	}
	
	protected void doPluginDestroy() {
		if(CollectionUtils.isEmpty(pluginInstances)) {
			return;
		}
		
		List<Plugin> plugins=new ArrayList<Plugin>(pluginInstances);
		OrderSorter.sort(plugins);
		
		for(Plugin plugin:plugins) {
			if(logger.isDebugEnabled()) {
				logger.debug("准备销毁插件- {}",plugin);
			}
			
			plugin.destroy();
			
			if(logger.isDebugEnabled()) {
				logger.debug("销毁插件- {}",plugin);
			}
		}
	}
	
	protected boolean isPlugin(Class<?> clazz) {
		return Plugin.class.isAssignableFrom(clazz);
	}
	
	protected boolean isPlugin(Object instance) {
		return (instance instanceof Plugin);
	}
	
	protected void doPluginProcess(Object instance) {
		if(logger.isDebugEnabled()) {
			logger.debug("找到插件 - {}",instance);
		}
		
		Plugin plugin=(Plugin) instance;
		
		if(logger.isDebugEnabled()) {
			logger.debug("准备初始化插件 - {}",instance);
		}
		
		plugin.init(redisCluster);
		
		if(logger.isDebugEnabled()) {
			logger.debug("初始化插件 - {}",instance);
		}
		
		pluginInstances.add(plugin);
	}

}